#include "bmp.h"

BMP::BMP(const string filename)
{
    fstream bmpfile;
    this->bmpname = filename;
    bitmap_header head;
    bitmap_info info;
    bmpfile.open(filename.c_str());
    if(!bmpfile.is_open())
    {
        cerr << "打开文件失败" << endl;
        exit(0);
    }

    bmpfile.seekg(0,ios_base::end);
    int zsize = bmpfile.tellg();
    bmpfile.seekg(0,ios_base::beg);
    this->bmpsize = zsize - sizeof(head) - sizeof(info);

    this->rgb = new char[this->bmpsize];

    bmpfile.read((char*)&head,sizeof(head));
    bmpfile.read((char*)&info,sizeof(info));
    bmpfile.read(rgb,bmpsize);
    bmpfile.close();
    this->width = info.width;
    this->height = info.height;
    this->bit = info.bit_count;
}
BMP::BMP(const string filename,LCD &lcd,int x)
{
    this->x = x;
    fstream bmpfile;
    this->bmpname = filename;
    bitmap_header head;
    bitmap_info info;
    bmpfile.open(filename.c_str());
    if(!bmpfile.is_open())
    {
        cerr << "打开文件失败" << endl;
        exit(0);
    }

    bmpfile.seekg(0,ios_base::end);
    int zsize = bmpfile.tellg();
    bmpfile.seekg(0,ios_base::beg);
    this->bmpsize = zsize - sizeof(head) - sizeof(info);

    this->rgb = new char[this->bmpsize];

    bmpfile.read((char*)&head,sizeof(head));
    bmpfile.read((char*)&info,sizeof(info));
    bmpfile.read(rgb,bmpsize);
    bmpfile.close();
    this->width = info.width;
    this->height = info.height;
    this->bit = info.bit_count;

    keep(lcd);
    management(lcd);
}
BMP::~BMP()
{
    delete [] rgb;
    delete [] bmpmen;
    // 释放为每个char*分配的内存  
    // for(int i = 0; i < x; ++i) {  
    //     for (int j = 0; j < x; ++j) {  
    //         delete[] pz[i][j]; // 释放内存  
    //         pz[i][j] = NULL; // 将指针设置为NULL以避免悬挂指针 
    //     }  
    // }
    cout << "bmp的析构函数成功调用" << endl;
}
void BMP::info()
{
    cout << "bmp图片的大小:" << width << "x" << height << endl;
    cout << "bmp图片数据大小:" << bmpsize << endl;
}
/*显示bmp图片*/
void BMP::display(LCD &lcd)
{
    info();
    //优化显示时的问题
    //设置可见区
    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    ioctl(lcd.fb,FBIOGET_VSCREENINFO,&vinfo);
    ioctl(lcd.fb,FBIOGET_FSCREENINFO,&finfo);
    //将初始可见区移到A     
    vinfo.xoffset = 0;
    vinfo.yoffset = 0 + lcd.high;
    ioctl(lcd.fb,FBIOPAN_DISPLAY,&vinfo);

    //显示图片
    memcpy(lcd.men,bmpmen,lcd.tosize);

    //图片加载完成后将可见区移回到B
    vinfo.xoffset = 0;
    vinfo.yoffset = 0;
    ioctl(lcd.fb,FBIOPAN_DISPLAY,&vinfo);
}
/*获取一个存储正确顺序的rgb指针*/
/*已在2号构造函数中调用*/
void BMP::keep(LCD &lcd)
{
    this->bmpmen = new char[lcd.tosize];
    //计算无效字节数
    int pad = ((4-( width * bit/8 ) %4)) % 4;
    //让rgb指向最末一行的首字节
    char * p;
    p = rgb + ( width * bit/8 + pad) * (height - 1);

    for(int x=0; x<height; x++)
    {
        //显示一行
        for(int y=0;y<width; y++)
        {
            int lcd_offset = (lcd.wide*x + y)*4;
            memcpy(bmpmen+lcd_offset, p , bit/8);
            p = p + bit/8;
        }
        p = p + pad;
        p = p - (width * bit/8 + pad) * 2;
    }
}
/*给二维数组分配目标*/
/*已在2号构造函数中调用*/
void BMP::management(LCD &lcd)
{
    this->tosize = lcd.tosize;
    int nwidth = width/x;
    int nheight = height/x;
    int p = nwidth * lcd.bpp/8;
    int q = width * lcd.bpp/8;

    // 设置图片二维数组的大小为x*x  
    pz.resize(x);  
    for (int i = 0; i < x; ++i) {  
        pz[i].resize(x);  
        for (int j = 0; j < x; ++j) {  
            pz[i][j] = NULL; 
        }  
    }

    // 设置int二维数组的大小为x*x
    /*并将1-x*x放入*/
    int m = 0;
    win.resize(x);  
    for (int i = 0; i < x; ++i) {  
        win[i].resize(x);  
        for (int j = 0; j < x; ++j) {  
            win[i][j] = ++m; 
        }
    }

    // 设置int二维数组的大小为x*x
    /*并将1-x*x放入*/
    m = 0;
    win2.resize(x);  
    for (int i = 0; i < x; ++i) {  
        win2[i].resize(x);  
        for (int j = 0; j < x; ++j) {  
            win2[i][j] = ++m; 
        }  
    }

    for(int n=0;n<x;n++)
    {
        for(int m=0;m<x;m++)
        {
            pz[n][m] = bmpmen + p*m + q*nheight*n;
        }
    }

}
/*将每个碎片显示到lcd上*/
void BMP::playman(LCD &lcd)
{
    int nwidth = width/x;
    int nheight = height/x;
    for(int i=0;i<nheight;i++)
    {
        int moffset = lcd.wide * lcd.bpp/8 * i;
        int loffset = width * lcd.bpp/8 * i;
        for(int j=0;j<nwidth;j++)
        {
            for(int m=0;m<x;m++)
            {
                for(int n=0;n<x;n++)
                {
                    memcpy(lcd.pm[m][n] + 4*j + moffset, pz[m][n] + 4*j + loffset, 4);
                }
            }
        }
    }
}
/*打乱二维数组*/
void BMP::randomize()
{
    // 设置随机种子（使用当前时间作为种子）  
    std::srand(static_cast<unsigned int>(std::time(0)));
    int p = 0;
    vector<char *>p1(x*x); //打乱图片
    vector<int>p2(x*x); //用来同步打乱win
    for(int i=0;i<x;i++)
    {
        for(int j=0;j<x;j++)
        {
            p2[p]   = win[i][j];
            p1[p++] = pz[i][j];
        }
    }
    /*需要确保两个二维数组打乱完全相同*/
    for(int i=0;i<20;i++)
    {
        int q = rand()%(x*x-2);
        char *s = p1[q];
        int s1 = p2[q];
        p1[q] = p1[x*x-1];
        p2[q] = p2[x*x-1];
        p1[x*x-1] = s;
        p2[x*x-1] = s1;
        s = NULL;
    }
    p = 0;
    for(int i=0;i<x;i++)
    {
        for(int j=0;j<x;j++)
        {
            win[i][j] = p2[p];
            pz[i][j] = p1[p++];
        }
    }
    for(int i=0;i<x;i++)
    {
        for(int j=0;j<x;j++)
        {
            cout << win[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}

/*通过区块编号得到二维数组下标*/
void BMP::move2(int a,int &b,int &c)
{
    for(int i=0;i<x;i++)
    {
        if( a>(i*x) && a<=((i+1)*x) )
        {
            b = i;
            c = (a - 1) - i*x;
            return;
        }
    }
}

/*图片碎片交换移动*/
void BMP::move(int a,int b,LCD &lcd)
{
    int e,f,g,h;
    move2(a,e,f);
    cout << "e:" << e << "f:" << f << endl;
    move2(b,g,h);
    cout << "g:" << g << "h:" << h << endl;
    /*交换两块图片并同时更新win*/
    char *s = NULL;
    int s1 = win[e][f];
    s = pz[e][f];
    win[e][f] = win[g][h];
    pz[e][f] = pz[g][h];
    win[g][h] = s1;
    pz[g][h] = s;

    int nwidth = width/x;
    int nheight = height/x;
    for(int i=0;i<nheight;i++)
    {
        int moffset = lcd.wide * lcd.bpp/8 * i;
        int loffset = width * lcd.bpp/8 * i;
        for(int j=0;j<nwidth;j++)
        {
            memcpy(lcd.pm[e][f] + 4*j + moffset, pz[e][f] + 4*j + loffset, 4);
            memcpy(lcd.pm[g][h] + 4*j + moffset, pz[g][h] + 4*j + loffset, 4);
        }
    }
    /*方便拼图*/
    for(int i=0;i<x;i++)
    {
        for(int j=0;j<x;j++)
        {
            cout << win[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}

/*判断胜利条件*/
bool BMP::ifwin()
{
    int w = 0;
    for(int i=0;i<x;i++)
    {
        for(int j=0;j<x;j++)
        {
            if(win[i][j] == win2[i][j])
                w++;
        }
    }
    if(w == (x*x))
        return true;
    else
        return false;
}